Initial support for automatic management of non-phy block devices.
4124b308O8yPHMKbj4YPR_grPGZmdA tools/check/chk
401d7e160vaxMBAUSLSicuZ7AQjJ3w tools/examples/Makefile
401d7e16UgeqroJQTIhwkrDVkoWgZQ tools/examples/README
+41597996VhTbNuHbuscYSfRb-WR6fA tools/examples/block-enbd
+41597996GHP2_yVih2UspXh328fgMQ tools/examples/block-file
405ff55dawQyCHFEnJ067ChPRoXBBA tools/examples/init.d/xend
40278d94cIUWl2eRgnwZtr4hTyWT1Q tools/examples/init.d/xendomains
40ee75a9xFz6S05sDKu-JCLqyVTkDA tools/examples/network
40c9c468IienauFHQ_xJIcqnPJ8giQ tools/python/xen/util/ip.py
4059c6a0pnxhG8hwSOivXybbGOwuXw tools/python/xen/util/tempfile.py
40c9c468SNuObE_YWARyS0hzTPSzKg tools/python/xen/xend/Args.py
+41597996WNvJA-DVCBmc0xU9w_XmoA tools/python/xen/xend/Blkctl.py
40c9c468Um_qc66OQeLEceIz1pgD5g tools/python/xen/xend/EventServer.py
40c9c468U8EVl0d3G--8YXVg6VJD3g tools/python/xen/xend/EventTypes.py
40c9c468QJTEuk9g4qHxGpmIi70PEQ tools/python/xen/xend/PrettyPrint.py
--- /dev/null
+#!/bin/sh
+
+# Usage: block-enbd [bind server ctl_port |unbind node]
+#
+# The file argument to the bind command is the file we are to bind to a
+# loop device. We print the path to the loop device node to stdout.
+#
+# The node argument to unbind is the name of the device node we are to
+# unbind.
+#
+# This assumes you're running a correctly configured server at the other end!
+
+case $1 in
+ bind)
+ for dev in /dev/nd*; do
+ if nbd-client $2:$3 $dev; then
+ echo $dev
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+
+ unbind)
+ nbd-client -d $2
+ exit 0
+ ;;
+
+ *)
+ echo 'Unknown command: ' $1
+ echo 'Valid commands are: bind, unbind'
+ exit 1
+esac
--- /dev/null
+#!/bin/sh
+
+# Usage: block_loop [bind file|unbind node]
+#
+# The file argument to the bind command is the file we are to bind to a
+# loop device. We print the path to the loop device node to stdout.
+#
+# The node argument to unbind is the name of the device node we are to
+# unbind.
+
+case $1 in
+ bind)
+ for dev in /dev/loop*; do
+ if losetup $dev $2; then
+ echo $dev
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+
+ unbind)
+ losetup -d $2
+ exit 0
+ ;;
+
+ *)
+ echo 'Unknown command: ' $1
+ echo 'Valid commands are: bind, unbind'
+ exit 1
+esac
# virtual interfaces. Specify 'yes' or 'no'.
(vif-antispoof no)
+# Setup script for file-backed block devices
+(block-file block-file)
+
+# Setup script for enbd-backed block devices
+(block-enbd block-enbd)
+
--- /dev/null
+"""Xend interface to block control scripts.
+"""
+import os
+import os.path
+import sys
+import string
+
+from xen.xend import XendRoot
+xroot = XendRoot.instance()
+
+"""Where network control scripts live."""
+SCRIPT_DIR = xroot.block_script_dir
+
+def block(op, type, dets, script=None):
+ """Call a block control script.
+ Xend calls this with op 'bind' when it is about to export a block device
+ (other than a raw partition). The script is called with unbind when a
+ device is no longer in use and should be removed.
+
+ @param op: operation (start, stop, status)
+ @param type: type of block device (determines the script used)
+ @param dets: arguments to the control script
+ @param script: block script name
+ """
+
+ if op not in ['bind', 'unbind']:
+ raise ValueError('Invalid operation:' + op)
+
+ # Special case phy devices - they don't require any (un)binding
+ if type == 'phy':
+ return dets
+
+ if script is None:
+ script = xroot.get_block_script(type)
+ script = os.path.join(SCRIPT_DIR, script)
+ args = [op] + string.split(dets, ':')
+ args = ' '.join(args)
+ out = os.popen(script + ' ' + args)
+
+ output = out.readline()
+ out.close()
+ return string.rstrip(output)
from XendError import VmError
+from server.blkif import blkdev_name_to_number
+
"""The length of domain names that Xen can handle.
The names stored in Xen itself are not used for much, and
xend can handle domain names of any length.
"""
return shutdown_reasons.get(code, "?")
-def blkdev_name_to_number(name):
- """Take the given textual block-device name (e.g., '/dev/sda1',
- 'hda') and return the device number used by the OS. """
-
- if not re.match( '^/dev/', name ):
- n = '/dev/' + name
- else:
- n = name
-
- try:
- return os.stat(n).st_rdev
- except:
- pass
-
- # see if this is a hex device number
- if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
- return string.atoi(name,16)
-
- return None
-
-def lookup_raw_partn(name):
- """Take the given block-device name (e.g., '/dev/sda1', 'hda')
- and return a dictionary { device, start_sector,
- nr_sectors, type }
- device: Device number of the given partition
- start_sector: Index of first sector of the partition
- nr_sectors: Number of sectors comprising this partition
- type: 'Disk' or identifying name for partition type
- """
-
- n = blkdev_name_to_number(name)
- if n:
- return [ { 'device' : n,
- 'start_sector' : long(0),
- 'nr_sectors' : long(1L<<63),
- 'type' : 'Disk' } ]
- else:
- return None
-
-def lookup_disk_uname(uname):
- """Lookup a list of segments for a physical device.
-
- @param uname: name of the device in the format \'phy:dev\' for a physical device
- @type uname: string
- @return: list of extents that make up the named device
- @rtype: [dict]
- """
- ( type, d_name ) = string.split( uname, ':' )
-
- if type == "phy":
- segments = lookup_raw_partn( d_name )
- else:
- segments = None
- return segments
-
def make_disk(vm, config, uname, dev, mode, recreate=0):
"""Create a virtual disk device for a domain.
@return: deferred
"""
idx = vm.next_device_index('vbd')
- segments = lookup_disk_uname(uname)
- if not segments:
- raise VmError("vbd: Segments not found: uname=%s" % uname)
- if len(segments) > 1:
- raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
- segment = segments[0]
# todo: The 'dev' should be looked up in the context of the domain.
vdev = blkdev_name_to_number(dev)
if not vdev:
raise VmError("vbd: Device not found: uname=%s dev=%s" % (uname, dev))
ctrl = xend.blkif_create(vm.dom, recreate=recreate)
- return ctrl.attachDevice(idx, config, vdev, mode, segment, recreate=recreate)
+ return ctrl.attachDevice(idx, config, uname, vdev, mode, recreate=recreate)
def vif_up(iplist):
"""send an unsolicited ARP reply for all non link-local IP addresses.
"""Where network control scripts live."""
network_script_dir = "/etc/xen/scripts"
+ """Where block control scripts live."""
+ block_script_dir = "/etc/xen/scripts"
+
logfile_default = "/var/log/xend.log"
loglevel_default = 'DEBUG'
def get_xend_address(self):
return self.get_config_value('xend-address', '')
+ def get_block_script(self, type):
+ return self.get_config_value('block-%s' % type, '')
+
def get_network_script(self):
return self.get_config_value('network-script', 'network')
from twisted.internet import defer
from xen.xend import sxp
+from xen.xend import Blkctl
from xen.xend.XendLogging import log
-from xen.xend.XendError import XendError
+from xen.xend.XendError import XendError, VmError
+import os
+import re
+import string
import channel
import controller
from messages import *
val.append(['uname', self.uname])
return val
+ def unbind(self):
+ log.debug("Unbinding block dev (type %s) from %s"
+ % (self.type, self.node))
+ Blkctl.block('unbind', self.type, self.node)
+
def destroy(self, change=0):
"""Destroy the device. If 'change' is true notify the front-end interface.
d = self.send_be_vbd_destroy()
if change:
d.addCallback(lambda val: self.interfaceChanged())
+ d.addCallback(lambda val: self.unbind())
def interfaceChanged(self):
"""Tell the back-end to notify the front-end that a device has been
backend.writeRequest(msg, response=d)
return d
-
+
+def blkdev_name_to_number(name):
+ """Take the given textual block-device name (e.g., '/dev/sda1',
+ 'hda') and return the device number used by the OS. """
+
+ if not re.match( '^/dev/', name ):
+ n = '/dev/' + name
+ else:
+ n = name
+
+ try:
+ return os.stat(n).st_rdev
+ except Exception, e:
+ print "blkdev_name_to_number> exception looking up device number for %s: %s" % (name, e)
+ pass
+
+ # see if this is a hex device number
+ if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
+ return string.atoi(name,16)
+
+ return None
+
+def lookup_raw_partn(name):
+ """Take the given block-device name (e.g., '/dev/sda1', 'hda')
+ and return a dictionary { device, start_sector,
+ nr_sectors, type }
+ device: Device number of the given partition
+ start_sector: Index of first sector of the partition
+ nr_sectors: Number of sectors comprising this partition
+ type: 'Disk' or identifying name for partition type
+ """
+
+ n = blkdev_name_to_number(name)
+ if n:
+ return [ { 'device' : n,
+ 'start_sector' : long(0),
+ 'nr_sectors' : long(1L<<63),
+ 'type' : 'Disk' } ]
+ else:
+ return None
+
class BlkifController(controller.SplitController):
"""Block device interface controller. Handles all block devices
for a domain.
self.devices[idx] = dev
return dev
- def attachDevice(self, idx, config, vdev, mode, segment, recreate=0):
+ def attachDevice(self, idx, config, uname, vdev, mode, recreate=0):
"""Attach a device to the specified interface.
On success the returned deferred will be called with the device.
@return: deferred
@rtype: Deferred
"""
+ if not recreate:
+ # Split into type and type-specific details (which are passed to the
+ # type-specific control script).
+ type, dets = string.split(uname, ':', 1)
+ # Special case: don't bother calling a script for phy. Could
+ # alternatively provide a "do nothing" script for phy devices...
+ node = Blkctl.block('bind', type, dets)
+
+ segments = lookup_raw_partn(node)
+
+ if not segments:
+ raise VmError("vbd: Segments not found: uname=%s" % uname)
+ if len(segments) > 1:
+ raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
+
+ segment = segments[0]
+
dev = self.addDevice(idx, config, vdev, mode, segment)
+
if recreate:
d = defer.succeed(dev)
else:
+ dev.node = node
+ dev.type = type
d = dev.attach()
return d